//
// Created by lau on 2019-11-11.
//

#ifndef VIDEOEFFECT_BEAUTYFILTER_H
#define VIDEOEFFECT_BEAUTYFILTER_H

#include "../context.h"
#include "../glutils/program.h"

namespace VideoEffect {

    class Context;

    class BeautyFilter {

    private:

        const char *VERTEXT_SHADER =
                "attribute vec2 aPosition;\n"
                "attribute vec2 aTextureCoord;\n"
                "varying vec2 vTexCoord;\n"
                "void main()\n"
                "{\n"
                "   gl_Position = vec4(aPosition, 0, 1.0);\n"
                "   vTexCoord = aTextureCoord;\n"
                "}\n";

        const char *FRAGMENT_SHADER =
                "precision mediump float;\n"
                "varying vec2 vTexCoord;\n"
                "uniform sampler2D uTexture0;\n"
                "uniform int width;\n"
                "uniform int height;\n"
                "uniform float opacity;\n"
                "void main()\n"
                "{\n"
                "   vec3 centralColor;\n"
                "   centralColor = texture2D(uTexture0, vTexCoord).rgb;\n"
                "   if(opacity < 0.01) {\n"
                "       gl_FragColor = vec4(centralColor, 1.0);\n"
                "   } else {\n"
                "       float x_a = float(width);\n"
                "       float y_a = float(height);\n"
                "       float mul_x = 2.0 / x_a;\n"
                "       float mul_y = 2.0 / y_a;\n"
                "       vec2 blurCoordinates0 = vTexCoord + vec2(0.0 * mul_x, -10.0 * mul_y);\n"
                "       vec2 blurCoordinates2 = vTexCoord + vec2(8.0 * mul_x, -5.0 * mul_y);\n"
                "       vec2 blurCoordinates4 = vTexCoord + vec2(8.0 * mul_x, 5.0 * mul_y);\n"
                "       vec2 blurCoordinates6 = vTexCoord + vec2(0.0 * mul_x, 10.0 * mul_y);\n"
                "       vec2 blurCoordinates8 = vTexCoord + vec2(-8.0 * mul_x, 5.0 * mul_y);\n"
                "       vec2 blurCoordinates10 = vTexCoord + vec2(-8.0 * mul_x, -5.0 * mul_y);\n"
                "       mul_x = 1.8 / x_a;\n"
                "       mul_y = 1.8 / y_a;\n"
                "       vec2 blurCoordinates1 = vTexCoord + vec2(5.0 * mul_x, -8.0 * mul_y);\n"
                "       vec2 blurCoordinates3 = vTexCoord + vec2(10.0 * mul_x, 0.0 * mul_y);\n"
                "       vec2 blurCoordinates5 = vTexCoord + vec2(5.0 * mul_x, 8.0 * mul_y);\n"
                "       vec2 blurCoordinates7 = vTexCoord + vec2(-5.0 * mul_x, 8.0 * mul_y);\n"
                "       vec2 blurCoordinates9 = vTexCoord + vec2(-10.0 * mul_x, 0.0 * mul_y);\n"
                "       vec2 blurCoordinates11 = vTexCoord + vec2(-5.0 * mul_x, -8.0 * mul_y);\n"
                "       mul_x = 1.6 / x_a;\n"
                "       mul_y = 1.6 / y_a;\n"
                "       vec2 blurCoordinates12 = vTexCoord + vec2(0.0 * mul_x,-6.0 * mul_y);\n"
                "       vec2 blurCoordinates14 = vTexCoord + vec2(-6.0 * mul_x,0.0 * mul_y);\n"
                "       vec2 blurCoordinates16 = vTexCoord + vec2(0.0 * mul_x,6.0 * mul_y);\n"
                "       vec2 blurCoordinates18 = vTexCoord + vec2(6.0 * mul_x,0.0 * mul_y);\n"
                "       mul_x = 1.4 / x_a;\n"
                "       mul_y = 1.4 / y_a;\n"
                "       vec2 blurCoordinates13 = vTexCoord + vec2(-4.0 * mul_x,-4.0 * mul_y);\n"
                "       vec2 blurCoordinates15 = vTexCoord + vec2(-4.0 * mul_x,4.0 * mul_y);\n"
                "       vec2 blurCoordinates17 = vTexCoord + vec2(4.0 * mul_x,4.0 * mul_y);\n"
                "       vec2 blurCoordinates19 = vTexCoord + vec2(4.0 * mul_x,-4.0 * mul_y);\n"
                "       float central;\n"
                "       float gaussianWeightTotal;\n"
                "       float sum;\n"
                "       float sampler;\n"
                "       float distanceFromCentralColor;\n"
                "       float gaussianWeight;\n"
                "       float distanceNormalizationFactor = 3.6;\n"
                "       central = texture2D(uTexture0, vTexCoord).g;\n"
                "       gaussianWeightTotal = 0.2;\n"
                "       sum = central * 0.2;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates0).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates1).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates2).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates3).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates4).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates5).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates6).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates7).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates8).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates9).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates10).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates11).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates12).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.1 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates13).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.1 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates14).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.1 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates15).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.1 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates16).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.1 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates17).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.1 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates18).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.1 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sampler = texture2D(uTexture0, blurCoordinates19).g;\n"
                "       distanceFromCentralColor = min(abs(central - sampler) * distanceNormalizationFactor, 1.0);\n"
                "       gaussianWeight = 0.1 * (1.0 - distanceFromCentralColor);\n"
                "       gaussianWeightTotal += gaussianWeight;\n"
                "       sum += sampler * gaussianWeight;\n"
                "       sum = sum/gaussianWeightTotal;\n"
                "       sampler = centralColor.g - sum + 0.5;\n"
                "       // 高反差保留\n"
                "       for(int i = 0; i < 5; ++i) {\n"
                "           if(sampler <= 0.5) {\n"
                "               sampler = sampler * sampler * 2.0;\n"
                "           } else {\n"
                "               sampler = 1.0 - ((1.0 - sampler)*(1.0 - sampler) * 2.0);\n"
                "           }\n"
                "       }\n"
                "       float aa = 1.0 + pow(sum, 0.3) * 0.09;\n"
                "       vec3 smoothColor = centralColor * aa - vec3(sampler) * (aa - 1.0);\n"
                "       smoothColor = clamp(smoothColor, vec3(0.0), vec3(1.0));\n"
                "       smoothColor = mix(centralColor, smoothColor, pow(centralColor.g, 0.33));\n"
                "       smoothColor = mix(centralColor, smoothColor, pow(centralColor.g, 0.39));\n"
                "       smoothColor = mix(centralColor, smoothColor, opacity);\n"
                "       gl_FragColor = vec4(pow(smoothColor, vec3(0.96)), 1.0);\n"
                "   }\n"
                "}\n";

        Program *mProgram;

        Context *mContext;

    public:
        void initFilter();

        void rendFrame(_VE_TEXTURE *textureIn, _VE_TEXTURE *textureOut);

        void setContext(Context *context);

        GLuint createTexture(int width, int height, VEUInt32 format, VEUInt32 target);
    };


}

#endif //VIDEOEFFECT_BEAUTYFILTER_H
